/*
 * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */
package com.oracle.svm.hosted.jdk;

import java.lang.reflect.Field;
import java.security.CodeSource;

import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.impl.InternalPlatform;
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;

import com.oracle.svm.core.FutureDefaultsOptions;
import com.oracle.svm.core.ParsingReason;
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
import com.oracle.svm.core.feature.InternalFeature;
import com.oracle.svm.core.jdk.ProtectionDomainSupport;
import com.oracle.svm.core.traits.BuiltinTraits.BuildtimeAccessOnly;
import com.oracle.svm.core.traits.BuiltinTraits.NoLayeredCallbacks;
import com.oracle.svm.core.traits.SingletonLayeredInstallationKind.Independent;
import com.oracle.svm.core.traits.SingletonTraits;
import com.oracle.svm.hosted.FeatureImpl;
import com.oracle.svm.hosted.FeatureImpl.AfterRegistrationAccessImpl;
import com.oracle.svm.hosted.ImageClassLoader;
import com.oracle.svm.util.ReflectionUtil;
import com.oracle.svm.util.TypeResult;

import jdk.graal.compiler.nodes.ValueNode;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderContext;
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugin;
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
import jdk.graal.compiler.nodes.util.ConstantFoldUtil;
import jdk.graal.compiler.phases.util.Providers;
import jdk.vm.ci.meta.JavaConstant;
import jdk.vm.ci.meta.JavaKind;
import jdk.vm.ci.meta.ResolvedJavaMethod;

@SingletonTraits(access = BuildtimeAccessOnly.class, layeredCallbacks = NoLayeredCallbacks.class, layeredInstallationKind = Independent.class)
@AutomaticallyRegisteredFeature
public class JDKInitializationFeature implements InternalFeature {
    private static final String JDK_CLASS_REASON = "Core JDK classes are initialized at build time";

    @Override
    public void afterRegistration(AfterRegistrationAccess access) {
        RuntimeClassInitializationSupport rci = ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
        rci.initializeAtBuildTime("com.sun.java.util.jar.pack", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.management", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.naming.internal", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.net.ssl", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.nio.file", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.nio.sctp", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.nio.zipfs", JDK_CLASS_REASON);

        rci.initializeAtBuildTime("java.io", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("java.lang", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("java.math", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("java.net", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("java.nio", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("java.text", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("java.time", JDK_CLASS_REASON);
        // see HijrahChronologyFeature for more details
        rci.initializeAtBuildTime("java.time.chrono.HijrahChronology", "Needs to be fully initialized at build time");
        rci.initializeAtBuildTime("java.util", JDK_CLASS_REASON);
        rci.initializeAtRunTime("java.util.concurrent.SubmissionPublisher", "Executor service must be recomputed");

        rci.initializeAtBuildTime("javax.annotation.processing", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.lang.model", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.management", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.naming", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.net", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.tools", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.xml", JDK_CLASS_REASON);

        rci.initializeAtBuildTime("jdk.management.jfr.internal.FlightRecorderMXBeanProvider$SingleMBeanComponent", "Ends up in the image heap with -H:Preserve=all");

        rci.initializeAtBuildTime("jdk.internal", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("jdk.jfr", "Needed for Native Image substitutions");
        rci.initializeAtRunTime("jdk.jfr.snippets.Snippets$HelloWorld", "Fails build-time initialization");
        rci.initializeAtRunTime("jdk.jfr.snippets.Snippets$HTTPPostRequest", "Fails build-time initialization");
        rci.initializeAtRunTime("jdk.jfr.snippets.Snippets$TransactionBlocked", "Fails build-time initialization");
        rci.initializeAtRunTime("jdk.jfr.snippets.Snippets$HTTPGetRequest", "Fails build-time initialization");
        rci.initializeAtBuildTime("jdk.net", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("jdk.nio", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("jdk.vm.ci", "Native Image classes are always initialized at build time");
        rci.initializeAtBuildTime("jdk.xml", JDK_CLASS_REASON);
        /*
         * The XML classes have cyclic class initializer dependencies, and class initialization can
         * deadlock/fail when initialization is started at the "wrong part" of the cycle.
         * Force-initializing the correct class of the cycle here, in addition to the
         * "whole package" initialization above, breaks the cycle because it triggers immediate
         * initilalization here before the static analysis is started.
         */
        rci.initializeAtBuildTime("jdk.xml.internal.JdkXmlUtils", JDK_CLASS_REASON);

        rci.initializeAtBuildTime("sun.invoke", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.launcher", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.management", JDK_CLASS_REASON);
        rci.initializeAtRunTime("sun.management.ManagementFactoryHelper$PlatformLoggingImpl", "Holds instances of LoggingMXBeanAccess");
        rci.initializeAtBuildTime("sun.misc", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.net", JDK_CLASS_REASON);

        rci.initializeAtBuildTime("sun.nio", JDK_CLASS_REASON);
        if (Platform.includedIn(InternalPlatform.WINDOWS_BASE.class)) {
            rci.initializeAtRunTime("sun.nio.ch.PipeImpl", "Contains SecureRandom reference, therefore can't be included in the image heap");
        }

        rci.initializeAtRunTime("sun.net.PortConfig", "Calls PortConfig.getLower0() and PortConfig.getUpper0()");

        /*
         * In the cases that java.io.ObjectInputFilter$Config#serialFilter field is needed, this
         * class needs to be initialized. Field is initialized in the static block of the Config
         * class in runtime, so we need to initialize class at run time. This change also makes us
         * create substitution for jdkSerialFilterFactory in the
         * com.oracle.svm.core.jdk.Target_jdk_internal_util_StaticProperty.
         */
        rci.initializeAtRunTime("java.io.ObjectInputFilter$Config", "Field filter have to be initialized at runtime");

        rci.initializeAtRunTime("sun.nio.ch.DevPollArrayWrapper", "Calls IOUtil.fdLimit()");
        rci.initializeAtRunTime("sun.nio.ch.EPoll", "Calls EPoll.eventSize(), EPoll.eventsOffset() and EPoll.dataOffset()");
        rci.initializeAtRunTime("sun.nio.ch.EPollSelectorImpl", "Calls IOUtil.fdLimit()");
        rci.initializeAtRunTime("sun.nio.ch.EventPortSelectorImpl", "Calls IOUtil.fdLimit()");
        rci.initializeAtRunTime("sun.nio.fs.LinuxWatchService$Poller", "LinuxWatchService.eventSize() and LinuxWatchService.eventOffsets()");

        rci.initializeAtBuildTime("sun.reflect", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.mscapi", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.text", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.util", JDK_CLASS_REASON);

        /* Minor fixes to make the list work */
        rci.initializeAtRunTime("com.sun.naming.internal.ResourceManager$AppletParameter", "Initializes AWT");
        rci.initializeAtBuildTime("java.awt.font.TextAttribute", "Required for sun.text.bidi.BidiBase.NumericShapings");
        rci.initializeAtBuildTime("java.awt.font.NumericShaper", "Required for sun.text.bidi.BidiBase.NumericShapings");
        rci.initializeAtBuildTime("java.awt.font.JavaAWTFontAccessImpl", "Required for sun.text.bidi.BidiBase.NumericShapings");

        /* FileSystemProviders related */
        if (FutureDefaultsOptions.fileSystemProvidersInitializedAtRunTime()) {
            rci.initializeAtRunTime("java.nio.file.spi", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);
            rci.initializeAtRunTime("sun.nio.fs", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);
            /* Extended*Option need to be registered at run time. */
            rci.initializeAtRunTime("com.sun.nio.file", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);
            /* Static references to ExtendedOptions. Needs to be run-time initialized. */
            rci.initializeAtRunTime("jdk.internal.misc.FileSystemOption", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);

            rci.initializeAtRunTime("java.nio.file.FileSystems", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);
            rci.initializeAtRunTime("java.nio.file.FileSystems$DefaultFileSystemHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);

            rci.initializeAtRunTime("java.util.zip.ZipFile$Source", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);
            rci.initializeAtRunTime("java.util.zip.ZipFile$Source", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);

            rci.initializeAtRunTime("java.io.FileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);
            rci.initializeAtRunTime("java.io.FileSystem$CurrentWorkingDirectoryHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);
            rci.initializeAtRunTime("java.io.UnixFileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);
            rci.initializeAtRunTime("java.io.WindowsFileSystem", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);

            /* Holder for the default file system. */
            rci.initializeAtRunTime("com.oracle.svm.core.jdk.runtimeinit.DefaultFileSystemHolder", FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON);

            /*
             * The following need to be build-time initialized because they can end up in the image
             * heap. There are substitutions to patch the links to run-time initialized classes like
             * FileSystem or FileSystemProvider.
             */

            /*
             * Require explicit initializeAtBuildTime because the sun.nio.fs is registered for
             * run-time initialization.
             */
            rci.initializeAtBuildTime("sun.nio.fs.UnixPath", "Allow UnixPath objects in the image heap (" + FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON + ")");
            rci.initializeAtBuildTime("sun.nio.fs.WindowsPath", "Allow WindowsPath objects in the image heap (" + FutureDefaultsOptions.RUN_TIME_INITIALIZE_FILE_SYSTEM_PROVIDERS_REASON + ")");
        }

        rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);

        /* Security services */
        rci.initializeAtBuildTime("com.sun.crypto.provider", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.security.auth", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.security.jgss", JDK_CLASS_REASON);
        rci.initializeAtRunTime("sun.security.jgss.wrapper.Krb5Util", "Holds the cleaner thread");
        rci.initializeAtBuildTime("com.sun.security.cert.internal.x509", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.security.ntlm", JDK_CLASS_REASON);

        rci.initializeAtBuildTime("java.security", JDK_CLASS_REASON);
        rci.initializeAtRunTime("sun.security.pkcs11.P11Util", "Cleaner reference");

        rci.initializeAtBuildTime("javax.crypto", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.security.auth", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.security.cert", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("javax.security.sasl", JDK_CLASS_REASON);

        rci.initializeAtBuildTime("sun.security.action", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.ec", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.internal.interfaces", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.internal.spec", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.jca", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.jgss", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("org.ietf.jgss.Oid", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("org.ietf.jgss.GSSException", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("org.ietf.jgss.GSSName", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.krb5", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.pkcs", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.pkcs10", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.pkcs12", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.provider", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.rsa", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.ssl", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.timestamp", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.tools", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.util", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.validator", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("sun.security.x509", JDK_CLASS_REASON);
        rci.initializeAtBuildTime("com.sun.jndi", JDK_CLASS_REASON);
        if (FutureDefaultsOptions.securityProvidersInitializedAtRunTime()) {
            rci.initializeAtRunTime("sun.security.ssl.SSLContextImpl", JDK_CLASS_REASON);
            rci.initializeAtRunTime("sun.security.ssl.SSLAlgorithmConstraints", JDK_CLASS_REASON);
        } else {
            rci.initializeAtBuildTime("sun.security.pkcs11", JDK_CLASS_REASON);
            rci.initializeAtBuildTime("sun.security.smartcardio", JDK_CLASS_REASON);
            rci.initializeAtBuildTime("com.sun.security.sasl", JDK_CLASS_REASON);
        }
        if (Platform.includedIn(Platform.DARWIN.class)) {
            rci.initializeAtBuildTime("apple.security", JDK_CLASS_REASON);
        }

        rci.initializeAtBuildTime("sun.rmi.transport.GC", "Loaded an unneeded library (rmi) in static initializer.");
        rci.initializeAtBuildTime("sun.rmi.transport.GC$LatencyLock", "Loaded an unneeded library (rmi) in static initializer.");

        rci.initializeAtRunTime("com.sun.jndi.dns.DnsClient", "Contains Random references, therefore can't be included in the image heap.");
        rci.initializeAtRunTime("sun.net.www.protocol.http.DigestAuthentication$Parameters", "Contains Random references, therefore can't be included in the image heap.");
        rci.initializeAtRunTime("sun.security.krb5.KrbServiceLocator", "Contains Random references, therefore can't be included in the image heap.");
        rci.initializeAtRunTime("com.sun.jndi.ldap.ServiceLocator", "Contains Random references, therefore can't be included in the image heap.");

        /*
         * The random number provider classes should be initialized at run time to reset their
         * values properly. Otherwise the numbers generated will be fixed for each generated image.
         */
        rci.initializeAtRunTime("java.lang.Math$RandomNumberGeneratorHolder", "Contains random seeds");
        rci.initializeAtRunTime("java.lang.StrictMath$RandomNumberGeneratorHolder", "Contains random seeds");

        rci.initializeAtRunTime("java.lang.ProcessImpl", "launchMechanism and helperpath for jspawnhelper should be computed at run-time");

        rci.initializeAtRunTime("jdk.internal.misc.InnocuousThread", "Contains a thread group INNOCUOUSTHREADGROUP.");
        rci.initializeAtRunTime("jdk.internal.util.StaticProperty", "Contains run time specific values.");

        rci.initializeAtRunTime("sun.nio.ch.Poller", "Contains an InnocuousThread.");
        rci.initializeAtRunTime("jdk.internal.jimage", "Pulls in direct byte buffers");

        rci.initializeAtRunTime("sun.net.www.protocol.jrt.JavaRuntimeURLConnection", "Pulls in jimage reader");

        rci.initializeAtRunTime("sun.launcher.LauncherHelper", "Pulls in jimage reader");

        rci.initializeAtRunTime("jdk.internal.foreign.abi.fallback.LibFallback$NativeConstants", "Fails build-time initialization");
        rci.initializeAtRunTime("jdk.internal.foreign.abi.fallback.FFIType", "Fails build-time initialization");
        rci.initializeAtRunTime("jdk.internal.foreign.abi.fallback.FFIABI", "Fails build-time initialization");
        rci.initializeAtRunTime("sun.reflect.misc.Trampoline", "Fails build-time initialization");

        rci.initializeAtRunTime("com.sun.org.apache.xml.internal.serialize.HTMLdtd", "Fails build-time initialization");

        rci.initializeAtRunTime("sun.security.ssl.SSLContextImpl$DefaultSSLContextHolder", "Stores secure random");
        rci.initializeAtRunTime("sun.security.ssl.SSLSocketFactoryImpl", "Stores secure random");
        rci.initializeAtRunTime("sun.security.provider.certpath.ssl.SSLServerCertStore", "Stores secure random");

        rci.initializeAtRunTime("jdk.internal.foreign.SystemLookup$WindowsFallbackSymbols", "Does not work on non-Windows modular images");

        rci.initializeAtRunTime("jdk.internal.logger.LoggerFinderLoader", "Contains a static field with a FilePermission value");

        rci.initializeAtRunTime("jdk.internal.markdown.MarkdownTransformer", "Contains a static field with a DocTreeScanner which is initialized at run time");

        /* Ensure "enhanced exception messages" are initialized (JDK 25+26, JDK-8348986). */
        var exceptionsClass = ReflectionUtil.lookupClass("jdk.internal.util.Exceptions");
        var exceptionsSetup = ReflectionUtil.lookupMethod(exceptionsClass, "setup");
        ReflectionUtil.invokeMethod(exceptionsSetup, null);

        /*
         * The local class Holder in FallbackLinker#getInstance fails the build time initialization
         * starting JDK 22. There is no way to obtain a list of local classes using reflection. They
         * are thus accessed by name. According to the code in Check.localClassName, the identifier
         * in the name should be continuous.
         */
        ImageClassLoader imageClassLoader = ((AfterRegistrationAccessImpl) access).getImageClassLoader();
        int i = 1;
        TypeResult<Class<?>> currentHolderClass = imageClassLoader.findClass("jdk.internal.foreign.abi.fallback.FallbackLinker$%dHolder".formatted(i));
        while (currentHolderClass.isPresent()) {
            rci.initializeAtRunTime(currentHolderClass.get(), "Fails build-time initialization");
            currentHolderClass = imageClassLoader.findClass("jdk.internal.foreign.abi.fallback.FallbackLinker$%dHolder".formatted(i++));
        }
    }

    @Override
    public void registerInvocationPlugins(Providers providers, GraphBuilderConfiguration.Plugins plugins, ParsingReason reason) {
        var enableNativeAccessClass = ReflectionUtil.lookupClass("java.lang.Module$EnableNativeAccess");
        InvocationPlugins.Registration r = new InvocationPlugins.Registration(plugins.getInvocationPlugins(), enableNativeAccessClass);
        r.register(new ModuleEnableNativeAccessPlugin());
    }

    /**
     * For modules in the image heap, {@code Module#enableNativeAccess} is fixed at build time. We
     * want to fold native access checks for build time modules to avoid run-time overheads as well
     * as pulling in the error handling code unnecessarily. For example: not folding this would
     * increase the size of hello worlds by 2x.
     * <p>
     * Why does this field impact the number of reachable methods so much?
     * {@link ProtectionDomainSupport} and related {@code ProtectionDomainFeature} have a
     * reachability handler for {@link CodeSource#getLocation}, which is not reachable by default in
     * helloworld, but becomes reachable if {@code Module#enableNativeAccess} is not folded. The
     * difference comes from {@code PosixNativeLibrarySupport#initializeBuiltinLibraries}, which
     * transitively calls into {@code Module#ensureNativeAccess} and if {@code enableNativeAccess}
     * is folded, the call to {@link CodeSource#getLocation} inside
     * {@code Module#ensureNativeAccess} can be removed as dead code during inlining before
     * analysis.
     * <p>
     * The modules for which this folding matters are {@code java.base} and
     * {@code org.graalvm.nativeimage.builder}), both of which have {@code enableNativeAccess}
     * already set by the time the builder starts running.
     */
    @Override
    public void beforeAnalysis(BeforeAnalysisAccess a) {
        var access = (FeatureImpl.BeforeAnalysisAccessImpl) a;
        access.allowStableFieldFoldingBeforeAnalysis(ModuleEnableNativeAccessPlugin.ENABLE_NATIVE_ACCESS_FIELD);

        // We force all Enum.hash fields to be eagerly computed.
        access.allowStableFieldFoldingBeforeAnalysis(access.findField(Enum.class, "hash"));

        // The fields below are initialized in their static initializers or as a part of vm startup.
        access.allowStableFieldFoldingBeforeAnalysis(access.findField(ModuleLayer.class, "EMPTY_LAYER"));
        access.allowStableFieldFoldingBeforeAnalysis(access.findField(System.class, "initialIn"));
        access.allowStableFieldFoldingBeforeAnalysis(access.findField(System.class, "initialErr"));
        access.allowStableFieldFoldingBeforeAnalysis(access.findField("java.util.jar.Attributes$Name", "KNOWN_NAMES"));
    }

    /**
     * Inlines calls to {@code Module$EnableNativeAccess#isNativeAccessEnabled()} if and only if
     * {@code Module#enableNativeAccess} is true. This is ok because the field is {@code @Stable},
     * meaning that a non-default value (i.e., {@code true}, will never change again. Thus, we can
     * constant-fold the call to enable optimizations, most importantly dead code elimination.
     */
    private static final class ModuleEnableNativeAccessPlugin extends InvocationPlugin.InlineOnlyInvocationPlugin {

        private static final Field ENABLE_NATIVE_ACCESS_FIELD = ReflectionUtil.lookupField(Module.class, "enableNativeAccess");

        ModuleEnableNativeAccessPlugin() {
            super("isNativeAccessEnabled", Module.class);
        }

        /**
         * See {@code java.lang.Module$EnableNativeAccess#isNativeAccessEnabled(Module target)}.
         */
        @Override
        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode targetNode) {
            JavaConstant moduleConstant = targetNode.asJavaConstant();
            if (moduleConstant != null) {
                var enableNativeAccessField = b.getMetaAccess().lookupJavaField(ENABLE_NATIVE_ACCESS_FIELD);
                if (enableNativeAccessField != null) {
                    var constant = ConstantFoldUtil.tryConstantFold(b.getConstantFieldProvider(), b.getConstantReflection(), b.getMetaAccess(),
                                    enableNativeAccessField, moduleConstant, b.getOptions(), targetMethod);
                    /*
                     * ConstantFoldUtil.tryConstantFold adheres to the @Stable field semantics,
                     * i.e., it only constant folds if the field has a non-default value (in this
                     * case `true`). See
                     * jdk.graal.compiler.core.common.spi.JavaConstantFieldProvider#
                     * readConstantField. In other words, if the field is `false`, `constant` would
                     * be null.
                     */
                    if (constant != null) {
                        /*
                         * Booleans are represented as int on the VM level so checking for int 1
                         * instead of boolean true.
                         */
                        assert constant.isJavaConstant() && constant.asJavaConstant().asInt() == 1 : "Must not constant fold if enableNativeAccess is false (@Stable semantics)";
                        b.push(JavaKind.Boolean, b.add(constant));
                        return true;
                    }
                }
            }
            return false;
        }
    }
}
